package step

import (
	"fmt"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type MpcPoint_Step struct {
	BaseMpcStep
	resultKeys []string
	signNum    int
	indexes    []big.Int
}

func CreateMpcPoint_Step(totalNum int, preValueKeys []string, resultKeys []string) *MpcPoint_Step {
	log.SyslogInfo("CreateMpcPoint_Step begin")

	signNum := len(preValueKeys)
	mpc := &MpcPoint_Step{*CreateBaseMpcStep(totalNum, signNum), resultKeys, signNum, []big.Int{}}

	for i := 0; i < signNum; i++ {
		mpc.messages[i] = createPointGeneratorBtc(preValueKeys[i])

	}

	return mpc
}

func (ptStep *MpcPoint_Step) CreateMessage() []mpcprotocol.StepMessage {
	log.SyslogDebug("MpcPoint_Step.CreateMessage begin")
	_, grpId, _ := osmconf.GetGrpId(ptStep.mpcResult)
	message := make([]mpcprotocol.StepMessage, ptStep.totalNum)

	for i := 0; i < ptStep.totalNum; i++ {
		message[i].MsgCode = mpcprotocol.MPCMessage
		nodeId, _ := osmconf.GetOsmConf().GetNodeIdByIndex(grpId, uint16(i))
		message[i].PeerID = nodeId
	}

	for i := 0; i < ptStep.signNum; i++ {
		pointer := ptStep.messages[i].(*mpcPointGeneratorBtc)
		for j := 0; j < ptStep.totalNum; j++ {
			message[j].Data = append(message[j].Data, pointer.seed[:]...)
		}
	}

	return message
}

func (ptStep *MpcPoint_Step) GetMsgGens() []MpcMessageGenerator {
	return ptStep.messages
}

func (ptStep *MpcPoint_Step) HandleMessage(msg *mpcprotocol.StepMessage) bool {
	log.SyslogInfo(fmt.Sprintf("..........MpcPoint_Step.HandleMessage begin, peerID:%s,ctx=%v StepId=%v", msg.PeerID.String(), ptStep.GetMpcContextId(), msg.StepId))

	result := ptStep.mpcResult
	_, grpIdString, _ := osmconf.GetGrpId(result)
	senderIndex, _ := osmconf.GetOsmConf().GetInxByNodeId(grpIdString, msg.PeerID)

	if len(msg.Data) != 2*ptStep.signNum {
		log.SyslogErr(fmt.Sprintf("MpcPoint_Step HandleMessage, msg data len doesn't match requiremant, dataLen:%d, ctx=%v", len(msg.Data), ptStep.GetMpcContextId()))
		return false
	}

	for i := 0; i < ptStep.signNum; i++ {
		pointer := ptStep.messages[i].(*mpcPointGeneratorBtc)
		_, exist := pointer.message[*msg.PeerID]
		if exist {
			log.SyslogErr(fmt.Sprintf("MpcPoint_Step.HandleMessage, get msg from seed fail. peer:%s", msg.PeerID.SlimString()))
			return false
		}
		// save pointData
		pointDataKey := mpcprotocol.BTCMpcPointData + "_" + strconv.Itoa(i) + "_" + strconv.Itoa(int(senderIndex))
		err := result.SetValue(pointDataKey, []big.Int{msg.Data[2*i+0], msg.Data[2*i+1]})
		if err != nil {
			log.SyslogErr("MpcPoint_Step err setValue", "key", pointDataKey, "ctx", ptStep.GetMpcContextId())
			return false
		}
		pointer.message[*msg.PeerID] = [2]big.Int{msg.Data[2*i+0], msg.Data[2*i+1]}
	}

	indexBig := big.NewInt(0).SetUint64(uint64(senderIndex))
	ptStep.indexes = append(ptStep.indexes, *indexBig)
	return true
}

func (ptStep *MpcPoint_Step) FinishStep(result mpcprotocol.MpcResultInterface, mpc mpcprotocol.StoremanManager) error {
	log.SyslogDebug("MpcPoint_Step.FinishStep begin")
	err := ptStep.BaseMpcStep.FinishStep()
	if err != nil {
		return err
	}

	for i := 0; i < ptStep.signNum; i++ {
		pointer := ptStep.messages[i].(*mpcPointGeneratorBtc)
		err = result.SetValue(ptStep.resultKeys[i], pointer.result[:])
		if err != nil {
			log.SyslogErr("MpcPoint_Step.FinishStep, SetValue fail. err:%s", err.Error())
			return err
		}
	}

	bigCollection, _ := osmconf.BuildDataByIndexes(&ptStep.indexes)
	err1 := result.SetValue(mpcprotocol.BTCMpcPointCollectoin, []big.Int{*bigCollection})
	log.SyslogInfo("MpcPoint_Step.FinishStep", "bigCollection", hexutil.Encode(bigCollection.Bytes()), "ctx", ptStep.GetMpcContextId())
	if err != nil {
		return err1
	}

	log.SyslogDebug("MpcPoint_Step.FinishStep succeed")
	return nil
}
